

import java.io.File;
import java.io.FileInputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

/**
 * 简单的UTF-8的编码解码工具类，用于替换系统的U8编码解码方式。
 * 声明：代码取自com.alibaba.fastjson.util.IOUtils，并在它的基础上做了一些优化工作。
 * 
 * @author heyongbo 834659942 2017/09/07
 */
public final class _UTF8 {

	/**
	 * 按utf-8编码：将char变为byte
	 * 
	 * @param src 待编码的char
	 * @param srcPos 开始索引
	 * @param srcLen char数目
	 * @param dst 编码后结果存储的目的地
	 * @return 返回-1编码失败；返回正整数为dst中 byte的长度
	 */
	public static int encode(final char[] src, int srcPos, final int srcLen, final byte[] dst) {
		int sl = srcPos + srcLen;
		int dp = 0;
		int dlASCII = dp + ((srcLen <= dst.length) ? srcLen : dst.length);

		// ASCII only optimized loop
		while (dp < dlASCII && src[srcPos] < '\u0080')
			dst[dp++] = (byte) src[srcPos++];

		char c, d;
		int uc, ip;
		while (srcPos < sl) {
			c = src[srcPos++];
			if (c < 0x80) {
				// Have at most seven bits
				dst[dp++] = (byte) c;
			} else if (c < 0x800) {
				// 2 bytes, 11 bits
				dst[dp++] = (byte) (0xc0 | (c >> 6));
				dst[dp++] = (byte) (0x80 | (c & 0x3f));
			} else if (c >= '\uD800' && c < ('\uDFFF' + 1)) { //Character.isSurrogate(c) but 1.7
				ip = srcPos - 1;
				if (c >= '\uD800' && c < ('\uDBFF' + 1)) { //if(Character.isHighSurrogate(c)){
					if (sl - ip < 2) {
						uc = -1;
					} else {
						d = src[ip + 1];
						if (d >= '\uDC00' && d < ('\uDFFF' + 1)) // if(Character.isLowSurrogate(d)){
							uc = ((c << 10) + d) + (0x010000 - ('\uD800' << 10) - '\uDC00'); // uc=Character.toCodePoint(c,d);
						else
							return -1; //throw new RuntimeException("encodeUTF8 error");
					}
				} else {
					if (c >= '\uDC00' && c < ('\uDFFF' + 1)) // if(Character.isLowSurrogate(c)){
						return -1; //throw new RuntimeException("encodeUTF8 error");
					else
						uc = c;
				}

				if (uc < 0) {
					dst[dp++] = (byte) '?';
				} else {
					dst[dp++] = (byte) (0xf0 | ((uc >> 18)));
					dst[dp++] = (byte) (0x80 | ((uc >> 12) & 0x3f));
					dst[dp++] = (byte) (0x80 | ((uc >> 6) & 0x3f));
					dst[dp++] = (byte) (0x80 | (uc & 0x3f));
					srcPos++; // 2 chars
				}
			} else {
				// 3 bytes, 16 bits
				dst[dp++] = (byte) (0xe0 | ((c >> 12)));
				dst[dp++] = (byte) (0x80 | ((c >> 6) & 0x3f));
				dst[dp++] = (byte) (0x80 | (c & 0x3f));
			}
		}
		return dp;
	}

	/**
	 * 按utf-8解码：将byte变为char
	 * 
	 * @param src 待解码的byte
	 * @param srcPos 开始索引
	 * @param srcLen byte数目
	 * @param dst 解码后结果存储的目的地
	 * @return 返回-1解码失败；返回正整数为dst中 char的长度
	 */
	public static int decode(final byte[] src, int srcPos, final int srcLen, final char[] dst) {
		final int sl = srcPos + srcLen;
		int dp = 0;
		int dlASCII = (srcLen <= dst.length) ? srcLen : dst.length;

		// ASCII only optimized loop
		while (dp < dlASCII && src[srcPos] >= 0)
			dst[dp++] = (char) src[srcPos++];

		char c;
		int b1, b2, b3, b4, uc;

		while (srcPos < sl) {
			b1 = src[srcPos++];
			if (b1 >= 0) {
				// 1 byte, 7 bits: 0xxxxxxx
				dst[dp++] = (char) b1;
			} else if ((b1 >> 5) == -2 && (b1 & 0x1e) != 0) {
				// 2 bytes, 11 bits: 110xxxxx 10xxxxxx
				if (srcPos < sl) {
					b2 = src[srcPos++];
					if ((b2 & 0xc0) != 0x80) // isNotContinuation(b2)
						return -1;
					else
						dst[dp++] = (char) (((b1 << 6) ^ b2) ^ (((byte) 0xC0 << 6) ^ ((byte) 0x80 << 0)));
					continue;
				}
				return -1;
			} else if ((b1 >> 4) == -2) {
				// 3 bytes, 16 bits: 1110xxxx 10xxxxxx 10xxxxxx
				if (srcPos + 1 < sl) {
					b2 = src[srcPos++];
					b3 = src[srcPos++];
					if ((b1 == (byte) 0xe0 && (b2 & 0xe0) == 0x80) //
							|| (b2 & 0xc0) != 0x80 //
							|| (b3 & 0xc0) != 0x80) { // isMalformed3(b1, b2, b3)
						return -1;
					} else {
						c = (char) ((b1 << 12) ^ (b2 << 6) ^ (b3 ^ (((byte) 0xE0 << 12) ^ ((byte) 0x80 << 6) ^ ((byte) 0x80 << 0))));
						if (c >= '\uD800' && c < ('\uDFFF' + 1))
							return -1;
						else
							dst[dp++] = c;
					}
					continue;
				}
				return -1;
			} else if ((b1 >> 3) == -2) {
				// 4 bytes, 21 bits: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
				if (srcPos + 2 < sl) {
					b2 = src[srcPos++];
					b3 = src[srcPos++];
					b4 = src[srcPos++];
					uc = ((b1 << 18) ^ (b2 << 12) ^ (b3 << 6) ^ (b4 ^ (((byte) 0xF0 << 18) ^ ((byte) 0x80 << 12) ^ ((byte) 0x80 << 6) ^ ((byte) 0x80 << 0))));
					if (((b2 & 0xc0) != 0x80 || (b3 & 0xc0) != 0x80 || (b4 & 0xc0) != 0x80) // isMalformed4
							||
							// shortest form check
							uc < 0x010000 || uc >= 0X10FFFF + 1) { // !Character.isSupplementaryCodePoint(uc)){
						return -1;
					} else {
						dst[dp++] = (char) ((uc >>> 10) + ('\uD800' - (0x010000 >>> 10))); // Character.highSurrogate(uc);
						dst[dp++] = (char) ((uc & 0x3ff) + '\uDC00'); // Character.lowSurrogate(uc);
					}
					continue;
				}
				return -1;
			} else {
				return -1;
			}
		}
		return dp;
	}

	public static void main_1_(String[] args) throws Exception {
		String str = "中国CN 浙江省ZJ 宁波市NingBo 江东区JiangDong";
		System.out.println(str);
		{
			char[] chs = str.toCharArray();
			byte[] raw = new byte[1024];
			int raw长度 = encode(chs, 0, chs.length, raw);
			String str编码结果 = new String(raw, 0, raw长度, "utf-8");
			System.out.println(str编码结果);
		}

		{
			//byte[] raw = str.getBytes("utf-8");
			byte[] raw = str.getBytes("gbk");
			char[] chs = new char[1024];
			int chs长度 = decode(raw, 0, raw.length, chs);
			String str解码结果 = new String(chs, 0, chs长度);
			System.out.println(str解码结果);
		}
	}

	/**
	 * 测试结果：编码 比系统要慢！五千慢6秒，一万次慢12秒，二万次慢25秒，三万次慢32秒；我HYBF可以接受！
	 */
	public static void main_2_(String[] args) throws Exception {
		final String str = getTestString();

		final int testCount = 1000;
		final Charset utf8 = StandardCharsets.UTF_8;

		char[] chs = str.toCharArray();
		byte[] raw = new byte[1024 * 1024 * 10];
		long t0, t1;

		t0 = System.nanoTime();
		for (int i = 0; i < testCount; i++) {
			if (!new String(raw, 0, encode(chs, 0, chs.length, raw), utf8).equals(str))
				throw new Error();
		}
		t1 = System.nanoTime();
		System.out.println("我HYB的utf8编码时间：\t" + (t1 - t0) / 1000000 + "ms, " + (t1 - t0));

		t0 = System.nanoTime();
		for (int i = 0; i < testCount; i++) {
			if (!new String(str.getBytes(utf8), utf8).equals(str))
				throw new Error();
		}
		t1 = System.nanoTime();
		System.out.println("系统的utf8编码时间：\t" + (t1 - t0) / 1000000 + "ms, " + (t1 - t0));

/*
测试50次的结果：
我HYB的utf8编码时间：	2172070
系统的utf8编码时间：		2411673

测试100次的结果：
我HYB的utf8编码时间：	3785623
系统的utf8编码时间：		4634568

测试150次的结果：
我HYB的utf8编码时间：	5745458
系统的utf8编码时间：		7441672

测试1000次的结果：
我HYB的utf8编码时间：	22230625
系统的utf8编码时间：		16977779

测试2000次的结果：
我HYB的utf8编码时间：	29412027
系统的utf8编码时间：		19120806

测试3000次的结果：
我HYB的utf8编码时间：	33591964
系统的utf8编码时间：		21643065

测试4000次的结果：
我HYB的utf8编码时间：	35907572
系统的utf8编码时间：		24669665

测试5000次的结果：
我HYB的utf8编码时间：	38855422
系统的utf8编码时间：		27783953
*/
	}

	/**
	 * 测试结果：解码 比系统要快！
	 */
	public static void main_3_(String[] args) throws Exception {
		final String str = getTestString();

		final int testCount = 5000;
		final Charset utf8 = StandardCharsets.UTF_8;

		byte[] raw = str.getBytes(utf8);
		char[] chs = new char[1024 * 1024 * 10];
		long t0, t1;

		t0 = System.nanoTime();
		for (int i = 0; i < testCount; i++) {
			if (!new String(chs, 0, decode(raw, 0, raw.length, chs)).equals(str))
				throw new Error();
		}
		t1 = System.nanoTime();
		System.out.println("我HYB的utf8解码时间:\t" + (t1 - t0) / 1000000 + "ms, " + (t1 - t0));

		t0 = System.nanoTime();
		for (int i = 0; i < testCount; i++) {
			if (!new String(raw, 0, raw.length, utf8).equals(str))
				throw new Error();
		}
		t1 = System.nanoTime();
		System.out.println("系统的utf8解码时间:\t" + (t1 - t0) / 1000000 + "ms, " + (t1 - t0));

/*
测试50次的结果：
我HYB的utf8解码时间:	981314
系统的utf8解码时间:	1564404

测试100次的结果：
我HYB的utf8解码时间:	1911243
系统的utf8解码时间:	2717740

测试150次的结果：
我HYB的utf8解码时间:	2847316
系统的utf8解码时间:	4320124

测试1000次的结果：
我HYB的utf8解码时间:	6091737
系统的utf8解码时间:	18020529

测试2000次的结果：
我HYB的utf8解码时间:	8910010
系统的utf8解码时间:	23981573

测试3000次的结果：
我HYB的utf8解码时间:	11795306
系统的utf8解码时间:	27357804

测试4000次的结果：
我HYB的utf8解码时间:	13996977
系统的utf8解码时间:	29288037

测试5000次的结果：
我HYB的utf8解码时间:	17281612
系统的utf8解码时间:	31804711
 */
	}

	public static void main/*_4_*/(String[] args) throws Exception {
		final String str = getTestString();

		char[] chs = str.toCharArray();

		byte[] raw = new byte[1024 * 1024 * 5];
		int rawLen = encode(chs, 0, chs.length, raw);

		chs = new char[1024 * 1024 * 5];
		int chsLen = decode(raw, 0, rawLen, chs);

		String str1 = new String(chs, 0, chsLen);
		System.out.println(str.equals(str1));
	}

	static String getTestString() throws Exception {
		String path = _UTF8.class.getResource("").getFile();
		File f = new File(path, "test.txt");
		FileInputStream fis = new FileInputStream(f);
		byte[] raw = new byte[1024 * 1024 * 10];
		int len = fis.read(raw);
		fis.close();
		if (len != f.length())
			throw new Error("len != f.length()");
		return new String(raw, 0, len, StandardCharsets.UTF_8);
	}

}
